//
//  NSObject+EV.m
//  EVClub
//
//  Created by sam on 14-8-22.
//  Copyright (c) 2014年 BitRice. All rights reserved.
//

static char kEx_Object_BR;

#import <objc/runtime.h>
#import "NSObject+EV.h"

inline id ev_castObj(id obj, Class aClass) {
    return [obj isKindOfClass:aClass] ? obj : nil;
}

@implementation NSObject (EV)
-(void)setEv_exObject:(NSObject *)exObject
{
    objc_setAssociatedObject(self, &kEx_Object_BR,exObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSObject *)ev_exObject
{
    return objc_getAssociatedObject(self, &kEx_Object_BR);
}

#pragma mark - 性能参数
-(void)ev_executeTime:(dispatch_block_t)block
{
    mach_timebase_info_data_t info;
    if (mach_timebase_info(&info) == KERN_SUCCESS)
    {
        uint64_t start = mach_absolute_time ();
        block ();
        uint64_t end = mach_absolute_time ();
        uint64_t elapsed = end - start;
        
        uint64_t nanos = elapsed * info.numer / info.denom;
        NSLog(@"amazing code took %f secconds",(CGFloat)nanos / NSEC_PER_SEC);
    }
}

#pragma mark - GCD 线程同步

-(void)ev_asyncTask:(dispatch_block_t)block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}

-(void)ev_syncTaskOnMain:(dispatch_block_t)block {
    dispatch_async(dispatch_get_main_queue(), block);
}

-(void) ev_asyncTask:(dispatch_block_t)block after:(NSTimeInterval)delay {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC),
                   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}

-(void) ev_syncTaskOnMain:(dispatch_block_t)block after:(NSTimeInterval)delay {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC),
                   dispatch_get_main_queue(), block);
}

-(void)ev_asyncTask:(dispatch_block_t)block returnOnMain:(dispatch_block_t)block2 {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        block();
        dispatch_async(dispatch_get_main_queue(), block2);
    });
}

-(void) ev_asyncTask:(dispatch_block_t)block afterExecBlock2:(dispatch_block_t)block2
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        block();
        block2();
    });
}


#pragma mark - helper

-(void) ev_execOnceOnKey:(NSString*)key block:(dispatch_block_t)block {
    NSUserDefaults* udef = [NSUserDefaults standardUserDefaults];
    BOOL processed = [udef boolForKey:key];
#ifdef DEBUG
    //#define test
#ifdef test
    processed = NO;
#endif
#endif
    if (!processed) {
        block();
        [udef setBool:YES forKey:key];
        [udef synchronize];
    }
}

#pragma mark - localization kit
- (void)ev_localizeOnView:(UIView*)view
       fromStringTable:(NSString*)table
           adjustLabel:(BOOL)adjustLabel
             skipViews:(NSArray*)skipViews
{
    for (UIView* v in view.subviews)
    {
        if (skipViews && skipViews.count > 0) {
            BOOL needSkip = NO;
            for (UIView* sv in skipViews) {
                if (sv == v) {
                    needSkip = YES;
                    break;
                }
            }
            if (needSkip) {
                continue ;
            }
        }
        
        if (v.subviews.count > 0)
        {
            [self ev_localizeOnView:v fromStringTable:table adjustLabel:adjustLabel skipViews:skipViews];
        }
        
        if ([v isKindOfClass:[UILabel class]])
        {
            UILabel* l = (UILabel*)v;
            if (adjustLabel) {
                NSTextAlignment alm = l.textAlignment;
                CGRect rcOrg = l.frame;
                CGPoint ptCenterOrg = l.center;
                l.text = NSLocalizedStringFromTable(l.text, table, nil);
                [l sizeToFit];
                if (alm == NSTextAlignmentCenter) {
                    l.center = ptCenterOrg;
                }
                else if (alm == NSTextAlignmentRight)
                {
                    CGRect rcNow = l.frame;
                    l.center = CGPointMake(rcOrg.origin.x+rcOrg.size.width-rcNow.size.width/2, ptCenterOrg.y);
                }
                else
                {
                    CGRect rcNow = l.frame;
                    l.center = CGPointMake(rcOrg.origin.x+rcNow.size.width/2, ptCenterOrg.y);
                }
            }
            else
            {
                l.text = NSLocalizedStringFromTable(l.text, table, nil);
            }
        }
        else if ([v isKindOfClass:[UIButton class]])
        {
            UIButton* b = (UIButton*)v;
            
            [b setTitle:NSLocalizedStringFromTable([b titleForState:UIControlStateNormal], table, nil)
               forState:UIControlStateNormal];
            
            [b setTitle:NSLocalizedStringFromTable([b titleForState:UIControlStateHighlighted], table, nil)
               forState:UIControlStateHighlighted];
            
            [b setTitle:NSLocalizedStringFromTable([b titleForState:UIControlStateDisabled], table, nil)
               forState:UIControlStateDisabled];
            
            [b setTitle:NSLocalizedStringFromTable([b titleForState:UIControlStateSelected], table, nil)
               forState:UIControlStateSelected];
        }
        else if ([v isKindOfClass:[UITextField class]])
        {
            UITextField* tf = (UITextField*)v;
            tf.text = NSLocalizedStringFromTable(tf.text, table, nil);
            tf.placeholder = NSLocalizedStringFromTable(tf.placeholder, table, nil);
        }
    }
}

- (NSString*)ev_locStringByKey:(NSString*)key
{
    return NSLocalizedStringFromTable(key, NSStringFromClass([self class]), nil);
}

+ (BOOL)ev_implInstanceMethodHookForClass:(__unsafe_unretained Class)targetClass
                        originalMethod:(SEL)originalMethod
                             newMethod:(SEL)newMethod
{
    Method mOrgMethod = class_getInstanceMethod(targetClass, originalMethod);
    if (!mOrgMethod) {
        return NO;
    }
    Method mNewMethod = class_getInstanceMethod(targetClass, newMethod);
    if (!mNewMethod) {
        return NO;
    }
    //如果直接使用下面的置换则会直接替换父类的方法，并不能实现真正的调换
    //method_exchangeImplementations(mOrgMethod, mNewMethod);
    
    //正确做法是增加一个临时的新方法名指向原有方法的实现体，然后互换各自的入口
    NSString* strOfNewMethod = [NSString stringWithFormat:@"_XL_TMP_EXCHANGE_%@",NSStringFromSelector(newMethod)];
    SEL selectorOfOrgBack = NSSelectorFromString(strOfNewMethod);
    class_addMethod(targetClass, selectorOfOrgBack, method_getImplementation(mOrgMethod), method_getTypeEncoding(mOrgMethod));
    Method mOrgBack = class_getInstanceMethod(targetClass, selectorOfOrgBack);
    
    class_replaceMethod(targetClass, originalMethod, method_getImplementation(mNewMethod), method_getTypeEncoding(mNewMethod));
    class_replaceMethod(targetClass, newMethod, method_getImplementation(mOrgBack), method_getTypeEncoding(mOrgBack));
    return YES;
}

+ (BOOL)ev_implClassMethodHookForClass:(__unsafe_unretained Class)targetClass
                     originalMethod:(SEL)originalMethod
                          newMethod:(SEL)newMethod
{
    Method mOrgMethod = class_getClassMethod(targetClass, originalMethod);
    if (!mOrgMethod) {
        return NO;
    }
    Method mNewMethod = class_getClassMethod(targetClass, newMethod);
    if (!mNewMethod) {
        return NO;
    }
    //如果直接使用下面的置换则会直接替换父类的方法，并不能实现真正的调换
    //method_exchangeImplementations(mOrgMethod, mNewMethod);
    
    //正确做法是增加一个临时的新方法名指向原有方法的实现体，然后互换各自的入口
    NSString* strOfNewMethod = [NSString stringWithFormat:@"_XL_TMP_EXCHANGE_%@",NSStringFromSelector(newMethod)];
    SEL selectorOfOrgBack = NSSelectorFromString(strOfNewMethod);
    class_addMethod(targetClass, selectorOfOrgBack, method_getImplementation(mOrgMethod), method_getTypeEncoding(mOrgMethod));
    Method mOrgBack = class_getClassMethod(targetClass, selectorOfOrgBack);
    
    class_replaceMethod(targetClass, originalMethod, method_getImplementation(mNewMethod), method_getTypeEncoding(mNewMethod));
    class_replaceMethod(targetClass, newMethod, method_getImplementation(mOrgBack), method_getTypeEncoding(mOrgBack));
    return YES;
}

+ (BOOL)ev_haveInstanceMethod:(SEL)selector
{
    Method method = class_getInstanceMethod([self class], selector);
    Method supMethod = class_getInstanceMethod([self superclass], selector);
    if (method != NULL && method != supMethod)
    {
        return YES;
    }
    return NO;
}

- (BOOL)ev_haveInstanceMethod:(SEL)selector
{
    return [[self class] ev_haveInstanceMethod:selector];
}
@end
